package com.hzy.system.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hzy.infrastructure.Tools;
import com.hzy.infrastructure.config.AdminAppConfig;
import com.hzy.infrastructure.domain.vo.PagingVo;
import com.hzy.system.domain.*;
import com.hzy.system.domain.bo.AccountContext;
import com.hzy.system.domain.consts.AdminFunctionConst;
import com.hzy.system.domain.dto.SysMenuTreeDto;
import com.hzy.system.domain.vo.*;
import com.hzy.system.mapper.ISysMenuMapper;
import com.hzy.system.mapper.ISysRoleMenuFunctionMapper;
import com.hzy.system.service.*;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author hzy
 * @since 2020-04-28
 */
@Service
public class SysMenuServiceImpl extends ServiceImpl<ISysMenuMapper, SysMenu> implements ISysMenuService {
    private final ISysRoleMenuFunctionMapper sysRoleMenuFunctionMapper;
    private final AdminAppConfig adminAppConfig;
    private final ISysFunctionService sysFunctionService;
    private final ISysMenuFunctionService sysMenuFunctionService;
    private final IAccountService accountService;
    private final ISysRoleMenuFunctionService sysRoleMenuFunctionService;

    public SysMenuServiceImpl(ISysRoleMenuFunctionMapper sysRoleMenuFunctionMapper,
                              AdminAppConfig adminAppConfig,
                              ISysFunctionService sysFunctionService,
                              ISysMenuFunctionService sysMenuFunctionService,
                              IAccountService accountService,
                              ISysRoleMenuFunctionService sysRoleMenuFunctionService) {

        this.sysRoleMenuFunctionMapper = sysRoleMenuFunctionMapper;
        this.adminAppConfig = adminAppConfig;
        this.sysFunctionService = sysFunctionService;
        this.sysMenuFunctionService = sysMenuFunctionService;
        this.accountService = accountService;
        this.sysRoleMenuFunctionService = sysRoleMenuFunctionService;
    }

    @Override
    public PagingVo<Map<String, Object>> findList(Integer page, Integer size, SysMenu search) {
        Page<Map<String, Object>> iPage = new Page<>(page, size);
        List<Map<String, Object>> data = this.baseMapper.getList(iPage, search);
        return PagingVo.page(iPage, data);
    }

    @Override
    public List<SysMenuTreeDto> getAll(SysMenu search) {
        return this.baseMapper.getAllList(search);
    }

    @Override
    public Map<String, Object> findForm(Integer id) {
        Map<String, Object> map = new HashMap<>(10);

        SysMenu form = this.baseMapper.selectById(id);
        form = Tools.nullSafe(form, new SysMenu());
        List<SysFunction> allFunctions = this.sysFunctionService.lambdaQuery()
                .orderByAsc(SysFunction::getNumber)
                .list();
        List<String> functionIds = this.sysMenuFunctionService.lambdaQuery()
                .eq(SysMenuFunction::getMenuId, id)
                .select(SysMenuFunction::getFunctionId)
                .list()
                .stream()
                .map(SysMenuFunction::getFunctionId)
                .collect(Collectors.toList());

        map.put("id", id);
        map.put("form", form);
        map.put("allFunctions", allFunctions);
        map.put("functionIds", functionIds);
        return map;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer saveForm(SysMenuVo form) {
        SysMenu model = form.getForm();
        List<String> functionIds = form.getFunctionIds();

        this.saveOrUpdate(model);

        if (ObjectUtils.isEmpty(model.getParentId()) || model.getParentId() == 0) {
            model.setLevelCode(model.getId().toString());
        } else {
            SysMenu parent = this.getById(model.getParentId());
            model.setLevelCode(parent.getLevelCode() + "." + model.getId());
        }

        this.saveOrUpdate(model);

        // 菜单功能绑定
        List<SysMenuFunction> menuFunctions = this.sysMenuFunctionService.lambdaQuery()
                .eq(SysMenuFunction::getMenuId, model.getId())
                .list();
        this.sysMenuFunctionService.remove(new LambdaQueryWrapper<SysMenuFunction>().eq(SysMenuFunction::getMenuId, model.getId()));
        if (!functionIds.isEmpty()) {
            for (String functionId : functionIds) {
                Optional<SysMenuFunction> sysMenuFunction = menuFunctions.stream()
                        .filter(w -> w.getFunctionId().equals(functionId))
                        .findFirst();

                SysMenuFunction menuFunctionModel = new SysMenuFunction();
                menuFunctionModel.setId(sysMenuFunction.isPresent() ? sysMenuFunction.get().getId() : Tools.getUUID());
                menuFunctionModel.setFunctionId(functionId);
                menuFunctionModel.setMenuId(model.getId());
                this.sysMenuFunctionService.save(menuFunctionModel);
            }
        }

        return model.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteList(List<Integer> ids) {
        for (Integer item : ids) {
            //删除当前菜单及一下的子集菜单
            SysMenu menu = this.getById(item);
            List<SysMenu> sysMenuList = this.lambdaQuery()
                    .eq(SysMenu::getLevelCode, menu.getLevelCode())
                    .or()
                    .likeLeft(SysMenu::getLevelCode, menu.getLevelCode() + ".")
                    .list();
            this.lambdaUpdate()
                    .eq(SysMenu::getLevelCode, menu.getLevelCode())
                    .or()
                    .likeLeft(SysMenu::getLevelCode, menu.getLevelCode() + ".")
                    .remove();
            //删除菜单关联表
            this.sysRoleMenuFunctionService.lambdaUpdate()
                    .in(SysRoleMenuFunction::getMenuId, sysMenuList
                            .stream()
                            .map(SysMenu::getId)
                            .collect(Collectors.toList())
                    ).remove();
            this.sysMenuFunctionService.lambdaUpdate()
                    .in(SysMenuFunction::getMenuId, sysMenuList
                            .stream()
                            .map(SysMenu::getId)
                            .collect(Collectors.toList())
                    ).remove();
        }
    }

    /**
     * 根据 当前用户 角色 获取 菜单列表
     *
     * @return result
     */
    @Override
    public List<SysMenu> getMenusByCurrentRole() {
        List<SysMenu> allMenuList = this.lambdaQuery().eq(SysMenu::getShow, true).orderByAsc(SysMenu::getNumber).list();

        AccountContext accountInfo = this.accountService.getAccountContext();

        if (accountInfo.getIsAdministrator()) {
            return allMenuList;
        }

        List<String> roleIds = accountInfo.getSysRoles().stream().map(SysRole::getId).collect(Collectors.toList());

        if (roleIds.isEmpty()) {
            return new ArrayList<>();
        }

        // 获取 菜单 集合
        List<SysMenu> oldMenuList = this.sysRoleMenuFunctionMapper.selectMenuByRoleId(roleIds);

        List<SysMenu> res = new ArrayList<>();

        for (SysMenu item : oldMenuList) {
            this.checkUpperLevel(allMenuList, oldMenuList, res, item);
            boolean any = res.stream().anyMatch(w -> w != null && w.getId().equals(item.getId()));
            if (!any) {
                res.add(item);
            }
        }

        // 正序 排序
        res = res.stream().sorted(Comparator.comparing(SysMenu::getNumber)).collect(Collectors.toList());

        return res;
    }

    /**
     * 辅助 函数 递归 查找 阶梯菜单
     *
     * @param allMenuList
     * @param oldMenuList
     * @param newMenuList
     * @param item
     */
    private void checkUpperLevel(List<SysMenu> allMenuList, List<SysMenu> oldMenuList, List<SysMenu> newMenuList, SysMenu item) {
        boolean anyOld = oldMenuList.stream().anyMatch(w -> w != null && w.getId().equals(item.getParentId()));
        boolean anyNew = newMenuList.stream().anyMatch(w -> w != null && w.getId().equals(item.getParentId()));
        if (!anyOld && !anyNew) {
            Optional<SysMenu> menuFirst = allMenuList.stream().filter(w -> w.getId().equals(item.getParentId())).findFirst();
            if (menuFirst.isPresent()) {
                SysMenu menu = menuFirst.get();
                if (!ObjectUtils.isEmpty(menu)) {
                    newMenuList.add(menu);
                    this.checkUpperLevel(allMenuList, oldMenuList, newMenuList, menu);
                }
            }
        }
    }

    /**
     * 创建菜单树
     *
     * @param id
     * @param sysMenuList
     * @return
     */
    @Override
    public List<SysMenuTreeDto> createMenus(Integer id, List<SysMenu> sysMenuList) {
        List<SysMenu> menus = ObjectUtils.isEmpty(id) || id == 0 ? sysMenuList
                .stream().filter(w -> ObjectUtils.isEmpty(w.getParentId()) || w.getParentId() == 0).collect(Collectors.toList())
                : sysMenuList.stream().filter(w -> id.equals(w.getParentId())).collect(Collectors.toList());

        List<SysMenuTreeDto> result = new ArrayList<>();
        for (SysMenu menu : menus) {
            SysMenuTreeDto sysMenuTreeDto = new SysMenuTreeDto();
            BeanUtils.copyProperties(menu, sysMenuTreeDto);
            sysMenuTreeDto.setChildren(this.createMenus(menu.getId(), sysMenuList));
            sysMenuTreeDto.setJumpUrl(ObjectUtils.isEmpty(menu.getJumpUrl()) ? menu.getRouter() : menu.getJumpUrl());
            result.add(sysMenuTreeDto);
        }
        return result;
    }

    /**
     * 根据当前用户 所拥有得 菜单 获取 每个菜单对应得权限
     *
     * @param menus
     * @return
     */
    @Override
    public List<Map<String, Object>> getPowerByMenus(List<SysMenu> menus) {
        AccountContext accountInfo = this.accountService.getAccountContext();
        List<SysFunction> functionList = this.sysFunctionService.lambdaQuery().orderByAsc(SysFunction::getNumber).list();
        List<SysMenuFunction> menuFunctionList = this.sysMenuFunctionService.lambdaQuery().list();
        List<Map<String, Object>> powerStateList = new ArrayList<>();
        // 管理员用户处理
        if (accountInfo.getIsAdministrator()) {
            for (SysMenu menu : menus) {
                Map<String, Object> powerStateMap = new HashMap<>(50);
                powerStateMap.put("menuId", menu.getId());
                for (SysFunction sysFunction : functionList) {
                    boolean isPower = menuFunctionList.stream()
                            .anyMatch(w -> w.getMenuId().equals(menu.getId()) && w.getFunctionId().equals(sysFunction.getId()));
                    if (AdminFunctionConst.DISPLAY.toLowerCase(Locale.ROOT).equals(sysFunction.getByName().toLowerCase(Locale.ROOT)) || this.adminAppConfig.getSystemMenuId().equals(menu.getParentId())) {
                        isPower = true;
                    }
                    powerStateMap.put(sysFunction.getByName().toLowerCase(Locale.ROOT), isPower);
                }
                powerStateList.add(powerStateMap);
            }
            return powerStateList;
        }
        // 非管理员用户处理
        List<SysRoleMenuFunction> roleMenuFunctionList = this.sysRoleMenuFunctionService.lambdaQuery()
                .in(SysRoleMenuFunction::getRoleId, accountInfo.getRoleIds())
                .list();
        for (SysMenu menu : menus) {
            Map<String, Object> powerStateMap = new HashMap<>(20);
            powerStateMap.put("menuId", menu.getId());
            for (SysFunction sysFunction : functionList) {
                if (accountInfo.getRoleIds().size() > 0) {
                    boolean isPower = roleMenuFunctionList.stream()
                            .anyMatch(w -> w.getMenuId().equals(menu.getId()) &&
                                    w.getFunctionId().equals(sysFunction.getId()) &&
                                    accountInfo.getRoleIds().contains(w.getRoleId()));
                    powerStateMap.put(sysFunction.getByName().toLowerCase(Locale.ROOT), isPower);
                } else {
                    powerStateMap.put(sysFunction.getByName().toLowerCase(Locale.ROOT), false);
                }
            }
            powerStateList.add(powerStateMap);
        }
        return powerStateList;
    }

    /**
     * 根据菜单Id 获取 按钮权限
     *
     * @param id
     * @return
     */
    @Override
    public Map<String, Boolean> getPowerStateByMenuId(Integer id) {
        AccountContext accountInfo = this.accountService.getAccountContext();
        SysMenu menu = this.baseMapper.selectById(id);
        List<SysFunction> functionList = this.sysFunctionService.lambdaQuery().orderByAsc(SysFunction::getNumber).list();
        List<SysMenuFunction> menuFunctionList = this.sysMenuFunctionService.lambdaQuery().eq(SysMenuFunction::getMenuId, id).list();
        List<SysRoleMenuFunction> roleMenuFunctionList = this.sysRoleMenuFunctionService.lambdaQuery()
                .in(SysRoleMenuFunction::getRoleId, accountInfo.getRoleIds()).list();
        Map<String, Boolean> map = new HashMap<>(100);

        // 如果是超级管理员
        if (accountInfo.getIsAdministrator()) {
            for (SysFunction item : functionList) {
                boolean isPower = menuFunctionList.stream()
                        .anyMatch(w -> w.getMenuId().equals(id) && Objects.equals(w.getFunctionId(), item.getId()));
                if (AdminFunctionConst.DISPLAY.toLowerCase(Locale.ROOT).equals(item.getByName().toLowerCase(Locale.ROOT)) || menu.getParentId().equals(adminAppConfig.getSystemMenuId())) {
                    isPower = true;
                }
                map.put(item.getByName().toLowerCase(Locale.ROOT), isPower);
            }
            return map;
        }

        for (SysFunction item : functionList) {
            if (!accountInfo.getRoleIds().isEmpty()) {
                boolean isPower = roleMenuFunctionList.stream()
                        .anyMatch(w -> w.getMenuId().equals(id) && w.getFunctionId().equals(item.getId()));
                map.put(item.getByName().toLowerCase(Locale.ROOT), isPower);
            } else {
                map.put(item.getByName().toLowerCase(Locale.ROOT), false);
            }
        }

        return map;
    }


}
